package me.numark;

import android.app.LoaderManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.RequestFuture;
import com.google.gson.Gson;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
import com.squareup.picasso.Picasso;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import butterknife.ButterKnife;
import butterknife.InjectView;
import me.numark.model.Message;
import me.numark.model.User;
import me.numark.net.BaseResponse;
import me.numark.net.GsonRequest;
import me.numark.net.NetworkConstants;
import me.numark.net.NetworkHelper;
import me.numark.providers.Numa;
import me.numark.utils.Global;
import me.numark.utils.MessageUtils;
import me.numark.utils.Settings;
import me.numark.utils.Utils;
import me.numark.view.CheckKeyboardLinearLayout;


public class ChatActivity extends BaseActivity implements LoaderManager.LoaderCallbacks<Cursor>,
        View.OnClickListener, CheckKeyboardLinearLayout.OnKeyboardShownListener,
        PullToRefreshListView.OnRefreshListener2<ListView> {

    public static final String EXTRA_DID = "did";
    public static final String EXTRA_USER_UID = "uid";
    public static final String EXTRA_USER_AVATAR = "pic";
    public static final String EXTRA_USER_NICK = "nick";

    private long did = 0;
    private User chatter = null;
    private User me;
    private int uid = 0;
    boolean fullLoaded = false;

    @InjectView(R.id.input_edit)
    EditText inputEdit;
    @InjectView(R.id.send_button)
    Button sendButton;
    @InjectView(R.id.ptr_list)
    PullToRefreshListView mList;
    @InjectView(R.id.back)
    View backButton;
    @InjectView(R.id.title)
    TextView title;
    @InjectView(R.id.check_keyboard)
    CheckKeyboardLinearLayout checkKeyboardLayout;

    ChatAdapter mAdapter;


    public static void startChat(Context context, User user) {
        Intent intent = new Intent(context, ChatActivity.class);
        intent.putExtra(EXTRA_USER_UID, user.uid);
        intent.putExtra(EXTRA_USER_AVATAR, user.pic);
        intent.putExtra(EXTRA_USER_NICK, user.nick);
        context.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        ButterKnife.inject(this);

        me = new Gson().fromJson(Settings.getProfile(), User.class);

        Intent intent = getIntent();
        did = intent.getLongExtra(EXTRA_DID, 0);
        uid = intent.getIntExtra(EXTRA_USER_UID, 0);
        String pic = intent.getStringExtra(EXTRA_USER_AVATAR);
        String nick = intent.getStringExtra(EXTRA_USER_NICK);
        if (!TextUtils.isEmpty(pic) && !TextUtils.isEmpty(nick)) {
            chatter = new User();
            chatter.uid = uid;
            chatter.pic = pic;
            chatter.nick = nick;
        }
        mAdapter = new ChatAdapter(this);
        mList.setAdapter(mAdapter);
        mList.setOnRefreshListener(this);
        title.setText("聊天");
        backButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        sendButton.setOnClickListener(this);
        checkKeyboardLayout.setListener(this);
        loadDialogInfo();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Global.currentChatUid = 0;
        clearBadge();
    }

    protected void clearBadge() {
        if (did == 0) {
            return;
        }
        /*
        boolean needClear = true;
        Cursor c = getContentResolver().query(Numa.DialogColumns.CONTENT_URI,
                new String[]{"badge"},
                "_id=" + did, null, null);
        if (c != null) {
            if (c.moveToFirst()) {
                if (c.getInt(0) != 0)
                    needClear = true;
                else
                    needClear = false;
            }
            c.close();
        }
        */
        ContentValues values = new ContentValues();
        values.put("badge", 0);
        getContentResolver().update(Numa.DialogColumns.CONTENT_URI, values, "_id=" + did, null);
        sendRead();
    }

    protected void sendRead() {
        String url = String.format(NetworkConstants.MSG_READ_URL, Settings.getUid());
        Map<String, String> body = new LinkedHashMap<String, String>();
        body.put("did", String.valueOf(did));
        url = url + "?" + GsonRequest.getQueryString(body);
        GsonRequest<BaseResponse> request = new GsonRequest<BaseResponse>(url,
                Request.Method.GET, BaseResponse.class, false, null, null, null);
        NetworkHelper.getInstance().addToRequestQueue(request);
        NetworkHelper.getInstance().getRequestQueue().start();
    }


    protected void loadDialogInfo() {
        String where;
        if (did > 0) {
            where = "_id=" + did;
        } else {
            where = "uid=" + uid;
        }
        Cursor cursor = getContentResolver().query(Numa.DialogColumns.CONTENT_URI, null, where, null, null);
        if (cursor.moveToFirst()) {
            did = cursor.getLong(cursor.getColumnIndex("_id"));
            fullLoaded = cursor.getInt(cursor.getColumnIndex("fullLoaded")) > 0;
            if (chatter == null) {
                chatter = new User();
                chatter.uid = cursor.getInt(cursor.getColumnIndex("uid"));
                chatter.pic = cursor.getString(cursor.getColumnIndex("pic"));
                chatter.nick = cursor.getString(cursor.getColumnIndex("nick"));
            }
        }
        Utils.closeQuietly(cursor);
        if (chatter != null) {
            afterLoadUserInfo();
        }
        getLoaderManager().restartLoader(ChatActivity.class.hashCode(), null, this);
        Global.currentChatUid = uid;
    }

    protected void afterLoadUserInfo() {
        title.setText(chatter.nick);
        uid = chatter.uid;
    }

    private void scrollToBottom() {
        ListView list = mList.getRefreshableView();
        Utils.scrollListView(list, mAdapter.getCount() - 1, 100);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(this, Numa.MessageColumns.CONTENT_URI, null, "did=" + did, null, "time asc");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        mAdapter.swapCursor(cursor);
        if (cursor.getCount() == 0) {
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    loadMoreMessages(did, Long.MAX_VALUE, chatter == null);
                    return null;
                }

                @Override
                protected void onPostExecute(Void aVoid) {
                    super.onPostExecute(aVoid);
                    scrollToBottom();
                }
            }.execute();
        } else {
            scrollToBottom();
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.send_button:
                sendMessage();
                break;
            case R.id.chat_avatar: {
                Intent intent = new Intent(this, ProfileActivity.class);
                intent.putExtra(ProfileActivity.EXTRA_UID, String.valueOf(((User) v.getTag()).uid));
                startActivity(intent);
            }
            break;
        }
    }

    private String getHash() {
        return Global.DeviceUniqueID + String.valueOf(System.currentTimeMillis() % 10000000);
    }

    void sendMessage() {
        String text = inputEdit.getText().toString().trim();
        if (text.length() == 0) {
            return;
        }
        String hash = getHash();
        showProgress("发送中");
        String url = String.format(NetworkConstants.MSG_SEND_URL, Settings.getUid());
        Map<String, String> body = new LinkedHashMap<String, String>();
        body.put("uid", Settings.getUid());
        body.put("reply_uid", String.valueOf(uid));
        body.put("description", text);
        body.put("hash", hash);
        if (did > 0) {
            body.put("did", String.valueOf(did));
        }
        GsonRequest<SendResponse> request = new GsonRequest<SendResponse>(url,
                Request.Method.POST, SendResponse.class, false, body, new Response.Listener<SendResponse>() {
            @Override
            public void onResponse(SendResponse response) {
                dismissProgress();
                inputEdit.setText("");
                if (response.data != null) {
                    MessageUtils.parseMessages(new Message[]{response.data}, getContentResolver());
                }
                MessageUtils.asyncUpdateMsg(ChatActivity.this);
                if (did == 0) {
                    did = response.data.did;
                    mList.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            loadDialogInfo();
                        }
                    }, 100);
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                dismissProgress();
                Toast.makeText(ChatActivity.this, "发送失败, 请稍后再试", Toast.LENGTH_SHORT).show();
            }
        });
        NetworkHelper.getInstance().addToRequestQueue(request);
        NetworkHelper.getInstance().getRequestQueue().start();
    }

    @Override
    public void onKeyboardShown() {
        scrollToBottom();
    }

    @Override
    public void onKeyboardHidden() {

    }

    @Override
    public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                if (did > 0) {
                    long beforeId = Long.MAX_VALUE;
                    if (mAdapter.getCount() > 0) {
                        Cursor cursor = (Cursor) mAdapter.getItem(0);
                        beforeId = cursor.getLong(cursor.getColumnIndex("_id"));
                    }
                    loadMoreMessages(did, beforeId, chatter == null);
                }
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                mList.onRefreshComplete();
            }
        }.execute();
    }

    @Override
    public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {

    }

    public static class SendResponse extends BaseResponse {

        public Message data;
    }

    public static class MessageResponse extends BaseResponse {

        public boolean has_more;

        public long timestamp;

        public Message[] data;

        public User user;
    }

    public boolean loadMoreMessages(long did, long mid, boolean userInfo) {
        String url = String.format(NetworkConstants.MSG_MESSAGES_URL, Settings.getUid(), did);
        Map<String, String> body = new LinkedHashMap<String, String>();
        body.put("before_mid", String.valueOf(mid));
        body.put("uid", Settings.getUid());
        body.put("userinfo", userInfo ? "1" : "0");
        body.put("limit", "30");
        url = url + "?" + GsonRequest.getQueryString(body);
        RequestFuture<MessageResponse> future = RequestFuture.newFuture();
        GsonRequest<MessageResponse> request = new GsonRequest<MessageResponse>(url,
                Request.Method.GET, MessageResponse.class, false, null, future, future);
        NetworkHelper.getInstance().addToRequestQueue(request);
        MessageResponse response;
        try {
            response = future.get();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        if (response == null || !response.ret || response.data == null) {
            return false;
        }
        if (userInfo && response.user != null) {
            chatter = response.user;
            afterLoadUserInfo();
        }
        if (!fullLoaded && !response.has_more && did > 0) {
            ContentValues values = new ContentValues();
            values.put("fullLoaded", true);
            getContentResolver().update(Numa.DialogColumns.CONTENT_URI, values, "_id=" + did, null);
        }
        MessageUtils.parseMessages(response.data, getContentResolver());
        return !response.has_more;
    }


    class ChatAdapter extends CursorAdapter {

        Context mContext;
        SparseIntArray timeFlagArr = new SparseIntArray();
        static final int timeWindow = 10 * 60;
        Date today, yesterday, year;

        public ChatAdapter(Context context) {
            super(context, null, 0);
            mContext = context;
            Date[] dates = Utils.getMarkDate();
            today = dates[0];
            yesterday = dates[1];
            year = dates[2];
        }

        @Override
        public int getItemViewType(int position) {
            Cursor cursor = (Cursor) getItem(position);
            int uid = cursor.getInt(cursor.getColumnIndex("uid"));
            if (uid == me.uid)
                return 0;
            return 1;
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            return null;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
        }

        public void bindView(View view, Context context, Cursor cursor, int position) {
            User user;
            int uid = cursor.getInt(cursor.getColumnIndex("uid"));
            if (uid == me.uid)
                user = me;
            else
                user = chatter;
            ImageView avatar = (ImageView) view.findViewById(R.id.chat_avatar);
            Picasso.with(ChatActivity.this).load(user.pic).into(avatar);
            avatar.setTag(user);
            avatar.setOnClickListener(ChatActivity.this);
            TextView content = (TextView) view.findViewById(R.id.chat_content);
            content.setText(cursor.getString(cursor.getColumnIndex("text")));
            long mid = cursor.getLong(cursor.getColumnIndex("_id"));
            long time = cursor.getLong(cursor.getColumnIndex("time"));
            TextView timeTextView = (TextView) view.findViewById(R.id.chat_time);
            boolean shouldShowTime = shouldShowTime(Math.abs(mid), time, cursor, position);
            if (shouldShowTime) {
                timeTextView.setVisibility(View.VISIBLE);
                String timeText = Utils.getTimeShowText(new Date(time), yesterday, today, year);
                timeTextView.setText(timeText);
            } else {
                timeTextView.setVisibility(View.GONE);
            }
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v;

            Cursor cursor = (Cursor) getItem(position);
            if (convertView == null) {
                int uid = cursor.getInt(cursor.getColumnIndex("uid"));
                if (uid == me.uid)
                    v = getLayoutInflater().inflate(R.layout.msg_right_content, parent, false);
                else
                    v = getLayoutInflater().inflate(R.layout.msg_left_content, parent, false);
            } else {
                v = convertView;
            }
            bindView(v, mContext, cursor, position);
            return v;
        }


        public void removeTimeFlag(long time) {
            time = time / 1000 / timeWindow;
            time = time % Integer.MAX_VALUE;
            int pos = timeFlagArr.indexOfKey((int) time);
            if (pos >= 0) {
                timeFlagArr.removeAt(pos);
            }
        }

        boolean shouldShowTime(long id, long time, Cursor c, int position) {
            if (id > 0 && time > 0) {
                time = time / 1000 / timeWindow;
                time = time % Integer.MAX_VALUE;
                int timeInt = (int) time;
                int minId = timeFlagArr.get(timeInt);
                int idInt = (int) (id % Integer.MAX_VALUE);
                if (minId == 0) {
                    timeFlagArr.put(timeInt, idInt);
                    shouldShowTime(0, 0, c, position - 1);
                    minId = timeFlagArr.get(timeInt);
                    return minId == idInt;
                } else {
                    if (minId == idInt) {
                        return true;
                    } else {
                        return false;
                    }
                }
            } else {
                if (position < 0) {
                    return false;
                }
                c.moveToPosition(position);
                time = c.getLong(c.getColumnIndex("time"));
                id = c.getLong(c.getColumnIndex("_id"));
                time = time / 1000 / timeWindow;
                time = time % Integer.MAX_VALUE;
                int timeInt = (int) time;
                int minId = timeFlagArr.get(timeInt);
                int idInt = (int) (id % Integer.MAX_VALUE);
                if (minId == 0) {
                    return false;
                } else {
                    if (minId > idInt) {
                        timeFlagArr.put(timeInt, idInt);
                        shouldShowTime(0, 0, c, position - 1);
                        return true;
                    }
                }
            }
            return false;
        }

    }
}
